using System;
using UnityEngine;

namespace RootMotion.FinalIK
{
	[Serializable]
	public class IKSolverLookAt : IKSolver
	{
		[Serializable]
		public class LookAtBone : Bone
		{
			public Vector3 forward
			{
				get
				{
					return transform.rotation * axis;
				}
			}

			public LookAtBone()
			{
			}

			public LookAtBone(Transform transform)
			{
				base.transform = transform;
			}

			public void Initiate(Transform root)
			{
				if (!(transform == null))
				{
					axis = Quaternion.Inverse(transform.rotation) * root.forward;
				}
			}

			public void LookAt(Vector3 direction, float weight)
			{
				Quaternion quaternion = Quaternion.FromToRotation(forward, direction);
				Quaternion rotation = transform.rotation;
				transform.rotation = Quaternion.Lerp(rotation, quaternion * rotation, weight);
			}
		}

		public Transform target;

		public LookAtBone[] spine = new LookAtBone[0];

		public LookAtBone head = new LookAtBone();

		public LookAtBone[] eyes = new LookAtBone[0];

		[Range(0f, 1f)]
		public float bodyWeight = 0.5f;

		[Range(0f, 1f)]
		public float headWeight = 0.5f;

		[Range(0f, 1f)]
		public float eyesWeight = 1f;

		[Range(0f, 1f)]
		public float clampWeight = 0.5f;

		[Range(0f, 1f)]
		public float clampWeightHead = 0.5f;

		[Range(0f, 1f)]
		public float clampWeightEyes = 0.5f;

		[Range(0f, 2f)]
		public int clampSmoothing = 2;

		public AnimationCurve spineWeightCurve = new AnimationCurve(new Keyframe(0f, 0.3f), new Keyframe(1f, 1f));

		private Vector3[] spineForwards = new Vector3[0];

		private Vector3[] headForwards = new Vector3[1];

		private Vector3[] eyeForward = new Vector3[1];

		private bool spineIsValid
		{
			get
			{
				if (spine == null)
				{
					return false;
				}
				if (spine.Length == 0)
				{
					return true;
				}
				for (int i = 0; i < spine.Length; i++)
				{
					if (spine[i] == null || spine[i].transform == null)
					{
						return false;
					}
				}
				return true;
			}
		}

		private bool spineIsEmpty
		{
			get
			{
				return spine.Length == 0;
			}
		}

		private bool headIsValid
		{
			get
			{
				if (head == null)
				{
					return false;
				}
				return true;
			}
		}

		private bool headIsEmpty
		{
			get
			{
				return head.transform == null;
			}
		}

		private bool eyesIsValid
		{
			get
			{
				if (eyes == null)
				{
					return false;
				}
				if (eyes.Length == 0)
				{
					return true;
				}
				for (int i = 0; i < eyes.Length; i++)
				{
					if (eyes[i] == null || eyes[i].transform == null)
					{
						return false;
					}
				}
				return true;
			}
		}

		private bool eyesIsEmpty
		{
			get
			{
				return eyes.Length == 0;
			}
		}

		public void SetLookAtWeight(float weight)
		{
			IKPositionWeight = Mathf.Clamp(weight, 0f, 1f);
		}

		public void SetLookAtWeight(float weight, float bodyWeight)
		{
			IKPositionWeight = Mathf.Clamp(weight, 0f, 1f);
			this.bodyWeight = Mathf.Clamp(bodyWeight, 0f, 1f);
		}

		public void SetLookAtWeight(float weight, float bodyWeight, float headWeight)
		{
			IKPositionWeight = Mathf.Clamp(weight, 0f, 1f);
			this.bodyWeight = Mathf.Clamp(bodyWeight, 0f, 1f);
			this.headWeight = Mathf.Clamp(headWeight, 0f, 1f);
		}

		public void SetLookAtWeight(float weight, float bodyWeight, float headWeight, float eyesWeight)
		{
			IKPositionWeight = Mathf.Clamp(weight, 0f, 1f);
			this.bodyWeight = Mathf.Clamp(bodyWeight, 0f, 1f);
			this.headWeight = Mathf.Clamp(headWeight, 0f, 1f);
			this.eyesWeight = Mathf.Clamp(eyesWeight, 0f, 1f);
		}

		public void SetLookAtWeight(float weight, float bodyWeight, float headWeight, float eyesWeight, float clampWeight)
		{
			IKPositionWeight = Mathf.Clamp(weight, 0f, 1f);
			this.bodyWeight = Mathf.Clamp(bodyWeight, 0f, 1f);
			this.headWeight = Mathf.Clamp(headWeight, 0f, 1f);
			this.eyesWeight = Mathf.Clamp(eyesWeight, 0f, 1f);
			this.clampWeight = Mathf.Clamp(clampWeight, 0f, 1f);
			clampWeightHead = this.clampWeight;
			clampWeightEyes = this.clampWeight;
		}

		public void SetLookAtWeight(float weight, float bodyWeight = 0f, float headWeight = 1f, float eyesWeight = 0.5f, float clampWeight = 0.5f, float clampWeightHead = 0.5f, float clampWeightEyes = 0.3f)
		{
			IKPositionWeight = Mathf.Clamp(weight, 0f, 1f);
			this.bodyWeight = Mathf.Clamp(bodyWeight, 0f, 1f);
			this.headWeight = Mathf.Clamp(headWeight, 0f, 1f);
			this.eyesWeight = Mathf.Clamp(eyesWeight, 0f, 1f);
			this.clampWeight = Mathf.Clamp(clampWeight, 0f, 1f);
			this.clampWeightHead = Mathf.Clamp(clampWeightHead, 0f, 1f);
			this.clampWeightEyes = Mathf.Clamp(clampWeightEyes, 0f, 1f);
		}

		public override void StoreDefaultLocalState()
		{
			for (int i = 0; i < spine.Length; i++)
			{
				spine[i].StoreDefaultLocalState();
			}
			for (int j = 0; j < eyes.Length; j++)
			{
				eyes[j].StoreDefaultLocalState();
			}
			if (head != null && head.transform != null)
			{
				head.StoreDefaultLocalState();
			}
		}

		public override void FixTransforms()
		{
			if (!(IKPositionWeight <= 0f))
			{
				for (int i = 0; i < spine.Length; i++)
				{
					spine[i].FixTransform();
				}
				for (int j = 0; j < eyes.Length; j++)
				{
					eyes[j].FixTransform();
				}
				if (head != null && head.transform != null)
				{
					head.FixTransform();
				}
			}
		}

		public override bool IsValid(ref string message)
		{
			if (!spineIsValid)
			{
				message = "IKSolverLookAt spine setup is invalid. Can't initiate solver.";
				return false;
			}
			if (!headIsValid)
			{
				message = "IKSolverLookAt head transform is null. Can't initiate solver.";
				return false;
			}
			if (!eyesIsValid)
			{
				message = "IKSolverLookAt eyes setup is invalid. Can't initiate solver.";
				return false;
			}
			if (spineIsEmpty && headIsEmpty && eyesIsEmpty)
			{
				message = "IKSolverLookAt eyes setup is invalid. Can't initiate solver.";
				return false;
			}
			Transform transform = IKSolver.ContainsDuplicateBone(spine);
			if (transform != null)
			{
				message = transform.name + " is represented multiple times in a single IK chain. Can't initiate solver.";
				return false;
			}
			Transform transform2 = IKSolver.ContainsDuplicateBone(eyes);
			if (transform2 != null)
			{
				message = transform2.name + " is represented multiple times in a single IK chain. Can't initiate solver.";
				return false;
			}
			return true;
		}

		public override Point[] GetPoints()
		{
			Point[] array = new Point[spine.Length + eyes.Length + ((head.transform != null) ? 1 : 0)];
			for (int i = 0; i < spine.Length; i++)
			{
				array[i] = spine[i];
			}
			int num = 0;
			for (int j = spine.Length; j < array.Length; j++)
			{
				array[j] = eyes[num];
				num++;
			}
			if (head.transform != null)
			{
				array[array.Length - 1] = head;
			}
			return array;
		}

		public override Point GetPoint(Transform transform)
		{
			LookAtBone[] array = spine;
			foreach (LookAtBone lookAtBone in array)
			{
				if (lookAtBone.transform == transform)
				{
					return lookAtBone;
				}
			}
			LookAtBone[] array2 = eyes;
			foreach (LookAtBone lookAtBone2 in array2)
			{
				if (lookAtBone2.transform == transform)
				{
					return lookAtBone2;
				}
			}
			if (head.transform == transform)
			{
				return head;
			}
			return null;
		}

		public bool SetChain(Transform[] spine, Transform head, Transform[] eyes, Transform root)
		{
			SetBones(spine, ref this.spine);
			this.head = new LookAtBone(head);
			SetBones(eyes, ref this.eyes);
			Initiate(root);
			return base.initiated;
		}

		protected override void OnInitiate()
		{
			if (firstInitiation || !Application.isPlaying)
			{
				if (spine.Length > 0)
				{
					IKPosition = spine[spine.Length - 1].transform.position + root.forward * 3f;
				}
				else if (head.transform != null)
				{
					IKPosition = head.transform.position + root.forward * 3f;
				}
				else if (eyes.Length > 0 && eyes[0].transform != null)
				{
					IKPosition = eyes[0].transform.position + root.forward * 3f;
				}
			}
			LookAtBone[] array = spine;
			foreach (LookAtBone lookAtBone in array)
			{
				lookAtBone.Initiate(root);
			}
			if (head != null)
			{
				head.Initiate(root);
			}
			LookAtBone[] array2 = eyes;
			foreach (LookAtBone lookAtBone2 in array2)
			{
				lookAtBone2.Initiate(root);
			}
			if (spineForwards == null || spineForwards.Length != spine.Length)
			{
				spineForwards = new Vector3[spine.Length];
			}
			if (headForwards == null)
			{
				headForwards = new Vector3[1];
			}
			if (eyeForward == null)
			{
				eyeForward = new Vector3[1];
			}
		}

		protected override void OnUpdate()
		{
			if (!(IKPositionWeight <= 0f))
			{
				IKPositionWeight = Mathf.Clamp(IKPositionWeight, 0f, 1f);
				if (target != null)
				{
					IKPosition = target.position;
				}
				SolveSpine();
				SolveHead();
				SolveEyes();
			}
		}

		private void SolveSpine()
		{
			if (!(bodyWeight <= 0f) && !spineIsEmpty)
			{
				Vector3 normalized = (IKPosition - spine[spine.Length - 1].transform.position).normalized;
				GetForwards(ref spineForwards, spine[0].forward, normalized, spine.Length, clampWeight);
				for (int i = 0; i < spine.Length; i++)
				{
					spine[i].LookAt(spineForwards[i], bodyWeight * IKPositionWeight);
				}
			}
		}

		private void SolveHead()
		{
			if (!(headWeight <= 0f) && !headIsEmpty)
			{
				Vector3 vector = ((spine.Length <= 0 || !(spine[spine.Length - 1].transform != null)) ? head.forward : spine[spine.Length - 1].forward);
				Vector3 normalized = Vector3.Lerp(vector, (IKPosition - head.transform.position).normalized, headWeight * IKPositionWeight).normalized;
				GetForwards(ref headForwards, vector, normalized, 1, clampWeightHead);
				head.LookAt(headForwards[0], headWeight * IKPositionWeight);
			}
		}

		private void SolveEyes()
		{
			if (!(eyesWeight <= 0f) && !eyesIsEmpty)
			{
				for (int i = 0; i < eyes.Length; i++)
				{
					Vector3 baseForward = ((!(head.transform != null)) ? eyes[i].forward : head.forward);
					GetForwards(ref eyeForward, baseForward, (IKPosition - eyes[i].transform.position).normalized, 1, clampWeightEyes);
					eyes[i].LookAt(eyeForward[0], eyesWeight * IKPositionWeight);
				}
			}
		}

		private Vector3[] GetForwards(ref Vector3[] forwards, Vector3 baseForward, Vector3 targetForward, int bones, float clamp)
		{
			if (clamp >= 1f || IKPositionWeight <= 0f)
			{
				for (int i = 0; i < forwards.Length; i++)
				{
					forwards[i] = baseForward;
				}
				return forwards;
			}
			float num = Vector3.Angle(baseForward, targetForward);
			float num2 = 1f - num / 180f;
			float num3 = ((!(clamp > 0f)) ? 1f : Mathf.Clamp(1f - (clamp - num2) / (1f - num2), 0f, 1f));
			float num4 = ((!(clamp > 0f)) ? 1f : Mathf.Clamp(num2 / clamp, 0f, 1f));
			for (int j = 0; j < clampSmoothing; j++)
			{
				float f = num4 * (float)Math.PI * 0.5f;
				num4 = Mathf.Sin(f);
			}
			if (forwards.Length == 1)
			{
				forwards[0] = Vector3.Slerp(baseForward, targetForward, num4 * num3);
			}
			else
			{
				float num5 = 1f / (float)(forwards.Length - 1);
				for (int k = 0; k < forwards.Length; k++)
				{
					forwards[k] = Vector3.Slerp(baseForward, targetForward, spineWeightCurve.Evaluate(num5 * (float)k) * num4 * num3);
				}
			}
			return forwards;
		}

		private void SetBones(Transform[] array, ref LookAtBone[] bones)
		{
			if (array == null)
			{
				bones = new LookAtBone[0];
				return;
			}
			if (bones.Length != array.Length)
			{
				bones = new LookAtBone[array.Length];
			}
			for (int i = 0; i < array.Length; i++)
			{
				if (bones[i] == null)
				{
					bones[i] = new LookAtBone(array[i]);
				}
				else
				{
					bones[i].transform = array[i];
				}
			}
		}
	}
}
